package com.iot.core.redis;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.iot.core.utils.ObjectUtils;
import com.iot.core.utils.SingletonFactory;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

/**
 * @ClassName JedisClusterClient
 * @Description
 * @Author DuWei
 * @Date 2019/1/24 11:32
 **/
public class JedisClusterClient implements IJedisClient {

    private Logger logger = LoggerFactory.getLogger(JedisClusterClient.class);

    private JedisCluster jedisCluster = SingletonFactory.getInstace(JedisCluster.class);

    /* (non-Javadoc)
     * @see com.jeeplus.common.redis.IJedisClient#get(java.lang.String)
     */
    @Override
    public String get(String key) {
        String value = null;
        try {
            if (jedisCluster.exists(key)) {
                value = jedisCluster.get(key);
                value = StringUtils.isNotBlank(value) && !"nil".equalsIgnoreCase(value) ? value : null;
            }
        } catch (Exception e) {
            logger.warn("get {} = {}", key, value, e);
        }
        return value;
    }

    @Override
    public byte[] get(byte[] key) {
        byte[] value = null;
        try {
            value = jedisCluster.get(key);
        } catch (Exception e) {
            logger.warn("setObject {} = {}", key, value, e);
        }
        return value;
    }

    /* (non-Javadoc)
     * @see com.jeeplus.common.redis.IJedisClient#getObject(java.lang.String)
     */
    @Override
    public Object getObject(String key) {
        Object value = null;
        try {
            if (jedisCluster.exists(getBytesKey(key))) {
                value = toObject(jedisCluster.get(getBytesKey(key)));
            }
        } catch (Exception e) {
            logger.warn("getObject {} = {}", key, value, e);
        }
        return value;
    }

    /* (non-Javadoc)
     * @see com.jeeplus.common.redis.IJedisClient#set(java.lang.String, java.lang.String, int)
     */
    @Override
    public String set(String key, String value, int cacheSeconds) {
        String result = null;
        try {
            result = jedisCluster.set(key, value);
            if (cacheSeconds != 0) {
                jedisCluster.expire(key, cacheSeconds);
            }
        } catch (Exception e) {
            logger.warn("set {} = {}", key, value, e);
        }
        return result;
    }

    @Override
    public byte[] set(byte[] key, byte[] value) {
        try {
            jedisCluster.set(key, value);
        } catch (Exception e) {
            logger.warn("set {} = {}", key, value, e);
        }
        return value;
    }

    @Override
    public byte[] set(byte[] key, byte[] value, int expire) {
        try {
            jedisCluster.set(key, value);
            jedisCluster.expire(key, expire);
        } catch (Exception e) {
            logger.warn("set {} = {}", key, value, e);
        }
        return value;
    }

    @Override
    public void del(byte[] key) {
        try {
            jedisCluster.del(key);
        } catch (Exception e) {
            logger.warn("set {}", key, e);
        }
    }

    @Override
    public Set<byte[]> keys(String pattern) {
        Set<byte[]> keys = null;
        try {
            keys = jedisCluster.hkeys(pattern.getBytes());
        } catch (Exception e) {
            logger.warn("keys {}", pattern, e);
        }
        return keys;
    }

    /* (non-Javadoc)
     * @see com.jeeplus.common.redis.IJedisClient#setObject(java.lang.String, java.lang.Object, int)
     */
    @Override
    public String setObject(String key, Object value, int cacheSeconds) {
        String result = null;
        try {
            result = jedisCluster.set(getBytesKey(key), toBytes(value));
            if (cacheSeconds != 0) {
                jedisCluster.expire(key, cacheSeconds);
            }
        } catch (Exception e) {
            logger.warn("setObject {} = {}", key, value, e);
        }
        return result;
    }

    /* (non-Javadoc)
     * @see com.jeeplus.common.redis.IJedisClient#getList(java.lang.String)
     */
    @Override
    public List<String> getList(String key) {
        List<String> value = null;
        try {
            if (jedisCluster.exists(key)) {
                value = jedisCluster.lrange(key, 0, -1);
            }
        } catch (Exception e) {
            logger.warn("getList {} = {}", key, value, e);
        }
        return value;
    }

    /* (non-Javadoc)
     * @see com.jeeplus.common.redis.IJedisClient#getObjectList(java.lang.String)
     */
    @Override
    public List<Object> getObjectList(String key) {
        List<Object> value = null;
        try {
            if (jedisCluster.exists(getBytesKey(key))) {
                List<byte[]> list = jedisCluster.lrange(getBytesKey(key), 0, -1);
                value = Lists.newArrayList();
                for (byte[] bs : list) {
                    value.add(toObject(bs));
                }
            }
        } catch (Exception e) {
            logger.warn("getObjectList {} = {}", key, value, e);
        }
        return value;
    }

    /* (non-Javadoc)
     * @see com.jeeplus.common.redis.IJedisClient#setList(java.lang.String, java.util.List, int)
     */
    @Override
    public long setList(String key, List<String> value, int cacheSeconds) {
        long result = 0;
        try {
            if (jedisCluster.exists(key)) {
                jedisCluster.del(key);
            }
            result = jedisCluster.rpush(key,value.toArray(new String[]{}));
            if (cacheSeconds != 0) {
                jedisCluster.expire(key, cacheSeconds);
            }
        } catch (Exception e) {
            logger.warn("setList {} = {}", key, value, e);
        }
        return result;
    }

    /* (non-Javadoc)
     * @see com.jeeplus.common.redis.IJedisClient#setObjectList(java.lang.String, java.util.List, int)
     */
    @Override
    public long setObjectList(String key, List<Object> value, int cacheSeconds) {
        long result = 0;
        try {
            if (jedisCluster.exists(getBytesKey(key))) {
                jedisCluster.del(key);
            }
            List<byte[]> list = Lists.newArrayList();
            for (Object o : value) {
                list.add(toBytes(o));
            }
            result = jedisCluster.rpush(getBytesKey(key), list.toArray(new byte[][]{}));
            if (cacheSeconds != 0) {
                jedisCluster.expire(key, cacheSeconds);
            }
        } catch (Exception e) {
            logger.warn("setObjectList {} = {}", key, value, e);
        }
        return result;
    }

    /* (non-Javadoc)
     * @see com.jeeplus.common.redis.IJedisClient#listAdd(java.lang.String, java.lang.String)
     */
    @Override
    public long listAdd(String key, String... value) {
        long result = 0;
        try {
            result = jedisCluster.rpush(key, value);
        } catch (Exception e) {
            logger.warn("listAdd {} = {}", key, value, e);
        }
        return result;
    }

    /* (non-Javadoc)
     * @see com.jeeplus.common.redis.IJedisClient#listObjectAdd(java.lang.String, java.lang.Object)
     */
    @Override
    public long listObjectAdd(String key, Object... value) {
        long result = 0;
        try {
            List<byte[]> list = Lists.newArrayList();
            for (Object o : value) {
                list.add(toBytes(o));
            }
            result = jedisCluster.rpush(getBytesKey(key), (byte[][]) list.toArray());
        } catch (Exception e) {
            logger.warn("listObjectAdd {} = {}", key, value, e);
        }
        return result;
    }

    /* (non-Javadoc)
     * @see com.jeeplus.common.redis.IJedisClient#getSet(java.lang.String)
     */
    @Override
    public Set<String> getSet(String key) {
        Set<String> value = null;
        try {
            if (jedisCluster.exists(key)) {
                value = jedisCluster.smembers(key);
            }
        } catch (Exception e) {
            logger.warn("getSet {} = {}", key, value, e);
        }
        return value;
    }

    /* (non-Javadoc)
     * @see com.jeeplus.common.redis.IJedisClient#getObjectSet(java.lang.String)
     */
    @Override
    public Set<Object> getObjectSet(String key) {
        Set<Object> value = null;
        try {
            if (jedisCluster.exists(getBytesKey(key))) {
                value = Sets.newHashSet();
                Set<byte[]> set = jedisCluster.smembers(getBytesKey(key));
                for (byte[] bs : set) {
                    value.add(toObject(bs));
                }
            }
        } catch (Exception e) {
            logger.warn("getObjectSet {} = {}", key, value, e);
        }
        return value;
    }

    /* (non-Javadoc)
     * @see com.jeeplus.common.redis.IJedisClient#setSet(java.lang.String, java.util.Set, int)
     */
    @Override
    public long setSet(String key, Set<String> value, int cacheSeconds) {
        long result = 0;
        try {
            if (jedisCluster.exists(key)) {
                jedisCluster.del(key);
            }
            result = jedisCluster.sadd(key, value.toString());
            if (cacheSeconds != 0) {
                jedisCluster.expire(key, cacheSeconds);
            }
        } catch (Exception e) {
            logger.warn("setSet {} = {}", key, value, e);
        }
        return result;
    }

    /* (non-Javadoc)
     * @see com.jeeplus.common.redis.IJedisClient#setObjectSet(java.lang.String, java.util.Set, int)
     */
    @Override
    public long setObjectSet(String key, Set<Object> value, int cacheSeconds) {
        long result = 0;
        try {
            if (jedisCluster.exists(getBytesKey(key))) {
                jedisCluster.del(key);
            }
            Set<byte[]> set = Sets.newHashSet();
            for (Object o : value) {
                set.add(toBytes(o));
            }
            result = jedisCluster.sadd(getBytesKey(key), (byte[][]) set.toArray());
            if (cacheSeconds != 0) {
                jedisCluster.expire(key, cacheSeconds);
            }
        } catch (Exception e) {
            logger.warn("setObjectSet {} = {}", key, value, e);
        }
        return result;
    }

    /* (non-Javadoc)
     * @see com.jeeplus.common.redis.IJedisClient#setSetAdd(java.lang.String, java.lang.String)
     */
    @Override
    public long setSetAdd(String key, String... value) {
        long result = 0;
        try {
            result = jedisCluster.sadd(key, value);
        } catch (Exception e) {
            logger.warn("setSetAdd {} = {}", key, value, e);
        }
        return result;
    }

    /**
     * 删除 namespace Set中的元素
     *
     * @param namespace Set的命名空间
     * @param value     Set中的元素
     * @return
     */
    @Override
    public long removeSetObject(String namespace, String... value) {
        long result = 0;
        try {
            result = jedisCluster.srem(namespace, value);
        } catch (Exception e) {
            logger.warn("removeSetObject {} = {}", namespace, value, e);
        }
        return result;
    }

    /**
     * namespace Set中是否包含元素value
     *
     * @param namespace
     * @param value
     * @return
     */
    @Override
    public boolean containsSetObject(String namespace, String value) {
        return jedisCluster.sismember(namespace, value);
    }

    /**
     * namespace Map中是否包含元素value
     *
     * @param namespace
     * @param value
     * @return
     */
    @Override
    public boolean containsMapObject(String namespace, String value) {
        return jedisCluster.hexists(namespace, value);
    }

    /* (non-Javadoc)
     * @see com.jeeplus.common.redis.IJedisClient#setSetObjectAdd(java.lang.String, java.lang.Object)
     */
    @Override
    public long setSetObjectAdd(String key, Object... value) {
        long result = 0;
        try {
            Set<byte[]> set = Sets.newHashSet();
            for (Object o : value) {
                set.add(toBytes(o));
            }
            result = jedisCluster.rpush(getBytesKey(key), (byte[][]) set.toArray());
        } catch (Exception e) {
            logger.warn("setSetObjectAdd {} = {}", key, value, e);
        }
        return result;
    }

    /* (non-Javadoc)
     * @see com.jeeplus.common.redis.IJedisClient#getMap(java.lang.String)
     */
    @Override
    public Map<String, String> getMap(String key) {
        Map<String, String> value = null;
        try {
            if (jedisCluster.exists(key)) {
                value = jedisCluster.hgetAll(key);
            }
        } catch (Exception e) {
            logger.warn("getMap {} = {}", key, value, e);
        }
        return value;
    }

    /* (non-Javadoc)
     * @see com.jeeplus.common.redis.IJedisClient#getObjectMap(java.lang.String)
     */
    @Override
    public Map<String, Object> getObjectMap(String key) {
        Map<String, Object> value = null;
        try {
            if (jedisCluster.exists(getBytesKey(key))) {
                value = Maps.newHashMap();
                Map<byte[], byte[]> map = jedisCluster.hgetAll(getBytesKey(key));
                for (Map.Entry<byte[], byte[]> e : map.entrySet()) {
                    value.put(ObjectUtils.toString(e.getKey()), toObject(e.getValue()));
                }
            }
        } catch (Exception e) {
            logger.warn("getObjectMap {} = {}", key, value, e);
        }
        return value;
    }

    @Override
    public Object getObjectMapValue(String namespace, String key) {
        Object value = null;
        try {
            if (jedisCluster.exists(getBytesKey(namespace))) {
                byte[] bytes = jedisCluster.hget(getBytesKey(namespace), getBytesKey(key));
                value = toObject(bytes);
            }
        } catch (Exception e) {
            logger.warn("getObjectMapValue {} = {}", key, value, e);
        }
        return value;
    }

    /* (non-Javadoc)
     * @see com.jeeplus.common.redis.IJedisClient#setMap(java.lang.String, java.util.Map, int)
     */
    @Override
    public String setMap(String key, Map<String, String> value, int cacheSeconds) {
        String result = null;
        try {
            if (jedisCluster.exists(key)) {
                jedisCluster.del(key);
            }
            if (null == value) {
                value = new HashMap<>();
            }
            //size小于1，初始化的时候会报错
            if (value.size() < 1) {
                value.put("", "");
            }
            result = jedisCluster.hmset(key, value);
            if (cacheSeconds != 0) {
                jedisCluster.expire(key, cacheSeconds);
            }
        } catch (Exception e) {
            logger.warn("setMap {} = {}", key, value, e);
        }
        return result;
    }

    /* (non-Javadoc)
     * @see com.jeeplus.common.redis.IJedisClient#setObjectMap(java.lang.String, java.util.Map, int)
     */
    @Override
    public String setObjectMap(String key, Map<String, Object> value, int cacheSeconds) {
        String result = null;
        try {
            if (jedisCluster.exists(getBytesKey(key))) {
                jedisCluster.del(key);
            }
            Map<byte[], byte[]> map = Maps.newHashMap();
            for (Map.Entry<String, Object> e : value.entrySet()) {
                map.put(getBytesKey(e.getKey()), toBytes(e.getValue()));
            }
            result = jedisCluster.hmset(getBytesKey(key), map);
            if (cacheSeconds != 0) {
                jedisCluster.expire(key, cacheSeconds);
            }
        } catch (Exception e) {
            logger.warn("setObjectMap {} = {}", key, value, e);
        }
        return result;
    }

    /**
     * 向 namespace 这个map内添加值
     *
     * @param namespace Map的命名空间
     * @param key       键
     * @param value     值
     * @return
     */
    @Override
    public Long mapPut(String namespace, String key, String value) {
        Long result = null;
        try {
            Map<String, String> map = jedisCluster.hgetAll(namespace);
            if (null == map) {
                jedisCluster.hmset(namespace, new HashMap<>());
            }
            result = jedisCluster.hset(namespace, key, value);
        } catch (Exception e) {
            logger.warn("mapPut {} = {}", key, value, e);
        }
        return result;
    }

    @Override
    public void mapNumAdd(String namespace, String key, Integer value) {
        try {
            Map<String, String> map = jedisCluster.hgetAll(namespace);
            if (null == map) {
                jedisCluster.hmset(namespace, new HashMap<>());
            }
            String mapvalue = map.get(key);
            if (null == mapvalue) {
                jedisCluster.hset(namespace, key, String.valueOf(value));
            } else {
                jedisCluster.hset(namespace, key, String.valueOf(mapvalue) + value);
            }
        } catch (Exception e) {
            logger.warn("mapPut {} = {}", key, value, e);
        }
    }

    @Override
    public String mapGet(String namespace, String key) {
        Map<String, String> map = jedisCluster.hgetAll(namespace);
        if (null == map) {
            return null;
        }
        return map.get(key);
    }

    /* (non-Javadoc)
     * @see com.jeeplus.common.redis.IJedisClient#mapObjectPut(java.lang.String, java.util.Map)
     */
    @Override
    public String mapObjectPut(String key, Map<String, Object> value) {
        String result = null;
        try {
            Map<byte[], byte[]> map = Maps.newHashMap();
            for (Map.Entry<String, Object> e : value.entrySet()) {
                map.put(getBytesKey(e.getKey()), toBytes(e.getValue()));
            }
            result = jedisCluster.hmset(getBytesKey(key), map);
        } catch (Exception e) {
            logger.warn("mapObjectPut {} = {}", key, value, e);
        }
        return result;
    }

    @Override
    public String mapObjectPut(String namespace, String key, Object value) {
        String result = null;
        try {
            if (!jedisCluster.exists(getBytesKey(namespace))) {
                Map<String,Object> map = Maps.newHashMap();
                map.put(key, value);
                this.setObjectMap(namespace, map, 0);
            }else {
                jedisCluster.hset(getBytesKey(namespace), getBytesKey(key), toBytes(value));
            }
        } catch (Exception e) {
            logger.warn("mapObjectPut {} = {}", key, value, e);
        }
        return result;
    }

    @Override
    public Object mapObjectGet(String namespace, String key) {
        Object result = null;
        try {
            byte[] bytes = jedisCluster.hget(getBytesKey(namespace), getBytesKey(key));
            if (null != bytes) {
                result = toObject(bytes);
            }
        } catch (Exception e) {
            logger.warn("mapObjectGet {} = {}", namespace, key, e);
        }
        return result;
    }

    /* (non-Javadoc)
     * @see com.jeeplus.common.redis.IJedisClient#mapRemove(java.lang.String, java.lang.String)
     */
    @Override
    public long mapRemove(String key, String mapKey) {
        long result = 0;
        try {
            result = jedisCluster.hdel(key, mapKey);
        } catch (Exception e) {
            logger.warn("mapRemove {}  {}", key, mapKey, e);
        }
        return result;
    }

    @Override
    public long mapRemoveByValue(String namespace, String mapValue) {
        Map<String, String> map = jedisCluster.hgetAll(namespace);
        if (null == map) {
            return 0;
        }
        if (null == mapValue) {
            return 0;
        }
        Iterator<Map.Entry<String, String>> iter = map.entrySet().iterator();
        long i = 0;
        while (iter.hasNext()) {
            Map.Entry<String,String> entry = iter.next();
            if (mapValue.equals(entry.getValue())) {
                iter.remove();
                i++;
            }
        }
        this.setMap(namespace, map, 0);
        return i;
    }

    /* (non-Javadoc)
     * @see com.jeeplus.common.redis.IJedisClient#mapObjectRemove(java.lang.String, java.lang.String)
     */
    @Override
    public long mapObjectRemove(String key, String mapKey) {
        long result = 0;
        try {
            result = jedisCluster.hdel(getBytesKey(key), getBytesKey(mapKey));
        } catch (Exception e) {
            logger.warn("mapObjectRemove {}  {}", key, mapKey, e);
        }
        return result;
    }

    /* (non-Javadoc)
     * @see com.jeeplus.common.redis.IJedisClient#mapExists(java.lang.String, java.lang.String)
     */
    @Override
    public boolean mapExists(String key, String mapKey) {
        boolean result = false;
        try {
            result = jedisCluster.hexists(key, mapKey);
        } catch (Exception e) {
            logger.warn("mapExists {}  {}", key, mapKey, e);
        }
        return result;
    }

    /* (non-Javadoc)
     * @see com.jeeplus.common.redis.IJedisClient#mapObjectExists(java.lang.String, java.lang.String)
     */
    @Override
    public boolean mapObjectExists(String key, String mapKey) {
        boolean result = false;
        try {
            result = jedisCluster.hexists(getBytesKey(key), getBytesKey(mapKey));
        } catch (Exception e) {
            logger.warn("mapObjectExists {}  {}", key, mapKey, e);
        }
        return result;
    }

    /* (non-Javadoc)
     * @see com.jeeplus.common.redis.IJedisClient#del(java.lang.String)
     */
    @Override
    public long del(String key) {
        long result = 0;
        try {
            if (jedisCluster.exists(key)) {
                result = jedisCluster.del(key);
            } else {
                logger.debug("del {} not exists", key);
            }
        } catch (Exception e) {
            logger.warn("del {}", key, e);
        }
        return result;
    }

    /* (non-Javadoc)
     * @see com.jeeplus.common.redis.IJedisClient#delObject(java.lang.String)
     */
    @Override
    public long delObject(String key) {
        long result = 0;
        try {
            if (jedisCluster.exists(getBytesKey(key))) {
                result = jedisCluster.del(getBytesKey(key));
            } else {
                logger.debug("delObject {} not exists", key);
            }
        } catch (Exception e) {
            logger.warn("delObject {}", key, e);
        }
        return result;
    }

    /* (non-Javadoc)
     * @see com.jeeplus.common.redis.IJedisClient#exists(java.lang.String)
     */
    @Override
    public boolean exists(String key) {
        boolean result = false;
        try {
            result = jedisCluster.exists(key);
        } catch (Exception e) {
            logger.warn("exists {}", key, e);
        }
        return result;
    }

    /* (non-Javadoc)
     * @see com.jeeplus.common.redis.IJedisClient#existsObject(java.lang.String)
     */
    @Override
    public boolean existsObject(String key) {
        boolean result = false;
        try {
            result = jedisCluster.exists(getBytesKey(key));
        } catch (Exception e) {
            logger.warn("existsObject {}", key, e);
        }
        return result;
    }


    /**
     * 获取byte[]类型Key
     *
     * @param object
     * @return
     */
    public static byte[] getBytesKey(Object object) {
        if (object instanceof String) {
            return ObjectUtils.getBytes((String) object);
        } else {
            return ObjectUtils.serialize(object);
        }
    }

    /**
     * Object转换byte[]类型
     *
     * @param object
     * @return
     */
    public static byte[] toBytes(Object object) {
        return ObjectUtils.serialize(object);
    }

    /**
     * byte[]型转换Object
     *
     * @param bytes
     * @return
     */
    public static Object toObject(byte[] bytes) {
        return ObjectUtils.unserialize(bytes);
    }

}
